package cn.kinoko.service.manga.impl;

import cn.hutool.core.util.StrUtil;
import cn.kinoko.common.base.model.BaseServiceImpl;
import cn.kinoko.common.code.ErrorCodeEnum;
import cn.kinoko.common.model.KV;
import cn.kinoko.common.utils.CollectionUtil;
import cn.kinoko.common.utils.ExceptionUtil;
import cn.kinoko.mapper.manga.MangaMarkMapper;
import cn.kinoko.model.cnTeam.constant.Position;
import cn.kinoko.model.cnTeam.vo.PositionTreeVO;
import cn.kinoko.model.manga.entity.MangaMark;
import cn.kinoko.model.manga.entity.MangaMarkComment;
import cn.kinoko.model.manga.export.TranslateDocxTemplateData;
import cn.kinoko.model.manga.vo.MarkCommentVO;
import cn.kinoko.model.manga.vo.MarkFullVO;
import cn.kinoko.service.manga.MangaCnMemberService;
import cn.kinoko.service.manga.MangaMarkCommentService;
import cn.kinoko.service.manga.MangaMarkService;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.comment.CommentRenderPolicy;
import com.deepoove.poi.plugin.comment.Comments;
import jakarta.annotation.Resource;
import org.apache.poi.util.LocaleUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author kinoko
 */
@Service
public class MangaMarkServiceImpl extends BaseServiceImpl<MangaMarkMapper, MangaMark> implements MangaMarkService {

    private static final InputStream TRANSLATE_TEMPLATE;

    static {
        TRANSLATE_TEMPLATE = MangaMarkServiceImpl.class.getClassLoader().getResourceAsStream("template/translate_docx_export_template.docx");
    }

    @Resource
    private MangaMarkCommentService mangaMarkCommentService;
    @Resource
    private MangaCnMemberService mangaCnMemberService;

    /**
     * 根据章节删除漫画
     *
     * @param epId 章节id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delByEpId(Long epId) {
        // 删除留言
        mangaMarkCommentService.delById(MangaMarkComment::getEpId, epId);
        // 删除标注
        lambdaUpdate().eq(MangaMark::getEpId, epId).remove();
    }

    /**
     * 根据漫画id删除漫画
     *
     * @param mangaId 漫画id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delByMangaId(Long mangaId) {
        // 删除留言
        mangaMarkCommentService.delById(MangaMarkComment::getMangaId, mangaId);
        // 删除标注
        lambdaUpdate().eq(MangaMark::getMangaId, mangaId).remove();
    }

    /**
     * 根据标注id删除标注
     *
     * @param ids 标注id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delByIds(List<Long> ids) {
        // 删除留言
        mangaMarkCommentService.delByMarkIds(ids);
        // 删除标注
        lambdaUpdate().in(MangaMark::getMarkId, ids).remove();
    }

    /**
     * 根据生肉页码id删除标注
     *
     * @param pageId 生肉页码id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delByRid(Long pageId) {
        // 删除留言
        mangaMarkCommentService.delById(MangaMarkComment::getEpRid, pageId);
        // 删除标注
        lambdaUpdate().eq(MangaMark::getEpRid, pageId).remove();
    }

    /**
     * 导出标注
     *
     * @param epId 章节id
     * @return 文件字节数组
     */
    @Override
    public byte[] exportMark(Long epId) {
        if (TRANSLATE_TEMPLATE == null) {
            throw new RuntimeException("can not find translate template docx");
        }
        // 获取标注数据
        TranslateDocxTemplateData data = getTranslateTemplateData(epId);
        ExceptionUtil.isNull(data, ErrorCodeEnum.SYSTEM_ERROR_B0408);

        // 构建map数据
        Map<String, Object> dataMap = buildTranslateDocxDataMap(data);

        // 配置批注插件并绑定模板
        Configure config = Configure.builder().bind("content", new CommentRenderPolicy()).build();
        XWPFTemplate compile = XWPFTemplate.compile(TRANSLATE_TEMPLATE, config);
        compile.render(dataMap);
        // 写入流
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            compile.writeAndClose(bos);
            return bos.toByteArray();
        } catch (IOException e) {
            log.error("[标注导出] doc制作失败", e);
            return null;
        }
    }

    /**
     * 构建翻译docx模板map数据
     *
     * @param data 模板数据
     * @return map数据
     */
    @NotNull
    private static Map<String, Object> buildTranslateDocxDataMap(TranslateDocxTemplateData data) {
        Map<String, Object> dataMap = new HashMap<>();
        // 基本成员信息
        dataMap.put("translator", data.getTranslator());
        dataMap.put("proofreader", data.getProofreader());
        dataMap.put("printer", data.getPrinter());

        // 组装每页的标记内容
        List<Map<String, Object>> pages = new ArrayList<>(data.getPages().size());
        for (TranslateDocxTemplateData.Page page : data.getPages()) {
            Map<String, Object> pageMap = new HashMap<>();
            // 页码
            pageMap.put("page", page.getPage());
            // 标记内容
            List<TranslateDocxTemplateData.Content> contents = page.getContents();
            if (CollectionUtil.isEmpty(contents)) continue;
            List<Map<String, Object>> contentsMaps = new ArrayList<>(page.getContents().size());
            for (TranslateDocxTemplateData.Content content : contents) {
                Map<String, Object> contentMap = new HashMap<>(1);
                String contentStr = content.getContent();
                Comments.CommentBuilder commentBuilder = Comments.of(contentStr);
                // 如果有评论则添加批注
                if (CollectionUtil.isNotEmpty(content.getComments())) {
                    for (TranslateDocxTemplateData.Comment comment : content.getComments()) {
                        commentBuilder.addSubComment(Comments.of()
                                .signature(comment.getName(), String.valueOf(comment.getName().charAt(0)), LocaleUtil.getLocaleCalendar())
                                .comment(comment.getComment())
                                .create());
                    }
                }
                contentMap.put("number", content.getNumber());
                contentMap.put("content", commentBuilder.create());
                contentsMaps.add(contentMap);
            }
            pageMap.put("contents", contentsMaps);
            pages.add(pageMap);
        }
        dataMap.put("pages", pages);
        return dataMap;
    }


    /**
     * 获取翻译模板数据
     *
     * @param epId 章节id
     * @return 翻译模板数据
     */
    private TranslateDocxTemplateData getTranslateTemplateData(Long epId) {
        // 查询标注
        List<MarkFullVO> markList = getBaseMapper().queryMarkFullVOByEpId(epId);
        if (markList.isEmpty()) return null;
        // 获取相关用户
        Set<Long> relevantMembers = getRelevantMember(markList);
        // 获取用户在该漫画中的职业分组
        Map<Long, PositionTreeVO> positionTreeMap = mangaCnMemberService.queryMangaPositionTree(markList.getFirst().getMangaId(), relevantMembers);

        // 准备模板数据
        TranslateDocxTemplateData data = new TranslateDocxTemplateData();
        // 获取翻译人员
        PositionTreeVO translatorTree = positionTreeMap.get(Position.TRANSLATOR.getCode());
        if (translatorTree != null) {
            // 填充翻译人员
            data.setTranslator(StrUtil.join(",", translatorTree.getChildren().stream().map(KV::getValue).toList()));
        }
        // 获取校对人员
        PositionTreeVO proofreaderTree = positionTreeMap.get(Position.PROOFREADER.getCode());
        if (proofreaderTree != null) {
            // 填充校对人员
            data.setProofreader(StrUtil.join(",", proofreaderTree.getChildren().stream().map(KV::getValue).toList()));
        }
        // 准备批注数据，根据标注分组
        Map<Long, List<MarkCommentVO>> commentMap = mangaMarkCommentService.queryCommentByEpId(epId).stream().collect(Collectors.groupingBy(MarkCommentVO::getMarkId));

        // 组装页码
        List<TranslateDocxTemplateData.Page> pages = markList.stream().collect(Collectors.groupingBy(MarkFullVO::getPage)).entrySet().parallelStream().map(entry -> {
            TranslateDocxTemplateData.Page page = new TranslateDocxTemplateData.Page();
            page.setPage(entry.getKey());

            // 组装页码内容
            List<TranslateDocxTemplateData.Content> contents = entry.getValue().stream()
                    // 根据标记序号排序
                    .sorted(Comparator.comparingInt(MarkFullVO::getNumber))
                    .map(mark -> {
                        TranslateDocxTemplateData.Content docContent = new TranslateDocxTemplateData.Content();
                        docContent.setNumber(mark.getNumber());
                        docContent.setText(mark.getText());
                        docContent.setTextAmend(mark.getTextAmend());

                        // 获取批注
                        List<MarkCommentVO> comments = commentMap.get(mark.getMarkId());
                        if (comments != null) {
                            List<TranslateDocxTemplateData.Comment> docComments = comments.stream().map(comment -> {
                                TranslateDocxTemplateData.Comment docComment = new TranslateDocxTemplateData.Comment();
                                docComment.setComment(comment.getComment());
                                docComment.setName(comment.getUserName());
                                return docComment;
                            }).toList();
                            // 填充批注
                            docContent.setComments(docComments);
                        }
                        return docContent;
                    }).toList();
            // 填充页码内容
            page.setContents(contents);
            return page;
            // 根据页码进行排序
        }).sorted(Comparator.comparing(TranslateDocxTemplateData.Page::getPage)).toList();
        // 填充页码
        data.setPages(pages);
        return data;
    }

    /**
     * 获取相关用户
     *
     * @param mangaMarkList 标注列表
     * @return 用户id集合
     */
    private Set<Long> getRelevantMember(List<MarkFullVO> mangaMarkList) {
        // 取出相关的用户id
        Set<Long> uidList = new HashSet<>();
        for (MarkFullVO mangaMark : mangaMarkList) {
            uidList.add(mangaMark.getCreateBy());
            uidList.add(mangaMark.getModifyBy());
            if (mangaMark.getProofreader() != null) {
                uidList.add(mangaMark.getProofreader());
            }
        }
        return uidList;
    }
}
